/*
 * @Author: WuFeng <763467339@qq.com>
 * @Date: 2024-05-08 17:27:25
 * @LastEditTime: 2025-10-16 15:25:51
 * @LastEditors: WuFeng <763467339@qq.com>
 * @Description: 
 * @FilePath: \vue-code-design\service\genderCode.js
 * @Copyright 版权声明
 */
var zip = require('express-zip');

// lodash
const _ = require('lodash')

// 文件处理模块
const fs = require('fs')
const path = require('path')

// 模板引擎
const dot = require('dot')

// 压缩zip
const archiver = require('archiver')

// 工具类
const {
  filesFind,
  getFileName,
  writeFileByUser,
  rmdir,
  responseData
} = require('../utils/index')

const { DEFAULT_OPTIONS, COLUMN_DEFAULT_OPTIONS, COLUMN_FORM_DEFAULT_OPTIONS } = require('../config/defaultOptions');
const { type } = require('os');

/**
 * 构造前端组件需要的数据 - 递归
 * @param Object data 元数据
 * @param Object defaultData 默认数据
 * @param Object treePath 递归层级的层级信息
 * @returns
 */
function buildOptions(data, defaultData, treePath) {
  if (typeof data !== 'object' || !data) {
    return data
  }
  if (Array.isArray(data)) {
    let treePathTemp = `${treePath}/{{index}}`
    const dataTemp = []
    for (const item of data) {
      // /column/{{index}}/dictData/{{index}}
      let defaultVal = {}
      if (treePath === '/column/{{index}}/dictData/{{index}}') {
        defaultVal = COLUMN_DEFAULT_OPTIONS['dictData'][0]
      }
      
      let itemRes = buildOptions(item, defaultVal, treePathTemp)
      if (JSON.stringify(itemRes) !== '{}') {
      
        if (treePath === '/column/{{index}}/rules') {
          const itemTemp = JSON.parse(JSON.stringify(itemRes))
          if (itemTemp.type !== '') {
            switch (itemTemp.type) {
              case 'required':
                itemRes = {
                  required: true,
                  message: itemTemp.message,
                  trigger: 'change'
                }
                break
              case 'regExp':
                itemRes = {
                  // pattern: new RegExp(itemTemp.pattern),
                  pattern: itemTemp.pattern,
                  message: itemTemp.message,
                  trigger: 'change'
                }
                break
            }
          }
        }

        dataTemp.push(itemRes)
      }
    }
    return dataTemp
  }
  const newData = {}
  for (const key in data) {
    let defaultVal = defaultData[key] || {}
    let treePathTemp = `${treePath}/${key}`
    // /column/{{index}}
    if (treePath.indexOf('/column/{{index}}') > -1) {
      // /column/{{index}}
      if (treePath === '/column/{{index}}') {
        defaultVal = COLUMN_DEFAULT_OPTIONS[key]
      }
      // /column/{{index}}/dictDataSourceApi
      if (treePath === '/column/{{index}}/dictDataSourceApi') {
        defaultVal = COLUMN_DEFAULT_OPTIONS['dictDataSourceApi'][key]
      }
      // /column/{{index}}/dictData/{{index}}
      if (treePath === '/column/{{index}}/dictData/{{index}}') {
        defaultVal = COLUMN_DEFAULT_OPTIONS['dictData'][0][key]
      }
      // /column/{{index}}/rules/{{index}}
      if (treePath === '/column/{{index}}/rules/{{index}}') {
        defaultVal = COLUMN_DEFAULT_OPTIONS['rules'][0][key]
      }
    }
    const newVal = buildOptions(data[key], defaultVal, treePathTemp)
    // TODO: 搁这儿判断值与默认值是否相等
    if (defaultVal !== newVal && JSON.stringify(newVal) !== '{}') {
      newData[key] = newVal
    }
  }
  return newData
}

/**
 * 生成代码接口
 * @param {*} sourceOptions 配置项元数据
 * @param {*} res http response 实例
 */
function genderCode(sourceOptions, res) {

  // 要检索的文件扩展名
  const extractArr = []
  
  // 其他配置
  const { codeRowSet, formCodeType } = sourceOptions.otherConfig || {}

  // 表单配置
  const formConfig = sourceOptions.formConfig || {}
  const hasCreate = formConfig?.hasCreate ?? true  // 是否生成新增
  const hasUpdate = formConfig?.hasUpdate ?? true  // 是否生成修改
  const hasDetail = formConfig?.hasDetail ?? true  // 是否生成详情

  // 构造前端组件需要的数据
  const newOptions = buildOptions(sourceOptions, DEFAULT_OPTIONS, '')

  // 处理列配置
  newOptions.column.map((item) => {
    // 如果不开启搜索框，删除搜索框相关属性
    if (typeof item.search === 'undefined' || item.search === false) {
      delete item.searchPlaceholder
      delete item.searchTip
    }
    // 如果不开启字典数据源，删除字典数据源相关属性
    if (typeof item.dictData === 'undefined' || item.dictData.length === 0) {
      delete item.dictData
    }
    // 如果不开启校验规则，删除校验规则相关属性
    if (typeof item.rules === 'undefined' || item.rules.length === 0) {
      delete item.rules
    }
  })

  // console.log(`🚀 ~ genderCode ~ newOptions-------------------------------------:`, JSON.stringify(newOptions))

  const { column: columnsResult, MODULE_VALUE } = newOptions || {}

  // 字段个数
  newOptions.columnNum = columnsResult.length || 0

  // 模板目录
  const templatesPath = path.resolve(__dirname, '../templates')
  // 缓存目录
  const cachePath = path.resolve(__dirname, '../cache')
  // 代码存放目录
  // const  codePath = 'vue3ElementPlus/DrawerDemo'
  const codePath = ''
  // 源 代码存放目录
  const sourceCodePath = path.resolve(templatesPath, codePath)
  // 目标 代码存放目录
  const targetCodePath = path.resolve(cachePath, codePath)

  // 删除缓存代码的文件夹
  rmdir(targetCodePath)

  // 模板需要用到的数据
  const tempData = Object.assign({}, {
    newOptions,
    sourceOptions
  })
  
  // java端(逢路)
  if (codeRowSet.includes('javaFengLu')) {
    tempData.tableInfo = sourceOptions.otherConfig.tableRowSet[0]
    extractArr.push('java')
    extractArr.push('xml')
  }

  // vue3ElementPlus声明式API
  if (codeRowSet.includes('vue3ElementPlus_Options_API')) {
    extractArr.push('vue')
    extractArr.push('js')
  }
    
  // 获取要生成的文件集合
  const fileList = filesFind({
    path: sourceCodePath,
    // path: 'D:\\web\\company\\vue3\\vue-code-design\\templates\\vue3ElementPlus\\DrawerDemo',  // 搜索路径 或者""F:\\Html" 注意要打'\\'，不能'\'要转义或者直接写'/'
    extract: extractArr, // 提取文件的名称，支持后缀查询，如果为空，则提取全部
    ignoreFile: [], // 忽略文件的名称，支持后缀查询 优先级大于extract
    isRecursion: true // 是否递归取文件目录下面的所有文件夹
  })

  // 遍历文件列表
  for (const item of fileList) {
    const dataStr = fs.readFileSync(item, 'utf-8')
    // 获取文件名
    const filename = getFileName(item)
    // 文件存放目的地
    const filePath = item.replace('templates', 'cache')
    const tempFn = dot.template(dataStr, {
      evaluate:    /\{\{\{([\s\S]+?)\}\}\}/g,
      interpolate: /\{\{\{=([\s\S]+?)\}\}\}/g,
      encode:      /\{\{\{!([\s\S]+?)\}\}\}/g,
      use:         /\{\{\{#([\s\S]+?)\}\}\}/g,
      define:      /\{\{\{##\s*([\w\.$]+)\s*(\:|=)([\s\S]+?)#\}\}\}/g,
      conditional: /\{\{\{\?(\?)?\s*([\s\S]*?)\s*\}\}\}/g,
      iterate:     /\{\{\{~\s*(?:\}\}\}|([\s\S]+?)\s*\:\s*([\w$]+)\s*(?:\:\s*([\w$]+))?\s*\}\}\})/g,
      varname: 'it',
      strip: false, // 解决替换掉换行问题
      append: true,
      selfcontained: false
    })
    const resultText = tempFn(tempData)

    // 生成 / 写入文件
    writeFileByUser(filePath, resultText)
  }

  /**
   * 因为是跨前后端分离，默认reponse header只能取到以下Content-Language、Content-Type、Expires、Last-Modified、Pragma5个默认值,
   * 要想取得其他的字段需要在服务器端设置Access-Control-Expose-Headers 配置前端想要获取的header。
   * 由于跨域 必须自定义header Access-Control-Expose-Headers 属性才会被暴露出来
   */
  res.header('Access-Control-Expose-Headers', 'Content-Disposition')
  // 打包输出 zip 流
  let zipFileList = []
  
  // java端(逢路)
  if (codeRowSet.includes('javaFengLu')) {
    const fileName = tempData.tableInfo.nameLargeHump || ''
    zipFileList = _.concat(zipFileList, [
      {
        path: path.resolve(__dirname, '../cache/javaFengLu/controller/TableNameController.java'),
        name: `/${MODULE_VALUE}/javaFengLu/${MODULE_VALUE}/controller/${fileName}Controller.java`
      },
      {
        path: path.resolve(__dirname, '../cache/javaFengLu/dto/TableNameDTO.java'),
        name: `/${MODULE_VALUE}/javaFengLu/${MODULE_VALUE}/dto/${fileName}DTO.java`
      },
      {
        path: path.resolve(__dirname, '../cache/javaFengLu/entity/TableName.java'),
        name: `/${MODULE_VALUE}/javaFengLu/${MODULE_VALUE}/entity/${fileName}.java`
      },
      {
        path: path.resolve(__dirname, '../cache/javaFengLu/mapper/TableNameMapper.xml'),
        name: `/${MODULE_VALUE}/javaFengLu/${MODULE_VALUE}/mapper/${fileName}Mapper.xml`
      },
      {
        path: path.resolve(__dirname, '../cache/javaFengLu/mapper/TableNameMapper.java'),
        name: `/${MODULE_VALUE}/javaFengLu/${MODULE_VALUE}/mapper/${fileName}Mapper.java`
      },
      {
        path: path.resolve(__dirname, '../cache/javaFengLu/service/impl/TableNameServiceImpl.java'),
        name: `/${MODULE_VALUE}/javaFengLu/${MODULE_VALUE}/service/I${fileName}ServiceImpl.java`
      },
      {
        path: path.resolve(__dirname, '../cache/javaFengLu/service/ITableNameService.java'),
        name: `/${MODULE_VALUE}/javaFengLu/${MODULE_VALUE}/service/I${fileName}Service.java`
      },
      {
        path: path.resolve(__dirname, '../cache/javaFengLu/vo/TableNameVo.java'),
        name: `/${MODULE_VALUE}/javaFengLu/${MODULE_VALUE}/vo/${fileName}Vo.java`
      },
      {
        path: path.resolve(__dirname, '../cache/javaFengLu/wrapper/TableNameWrapper.java'),
        name: `/${MODULE_VALUE}/javaFengLu/${MODULE_VALUE}/wrapper/${fileName}Wrapper.java`
      },
    ])
  }

  // vue3ElementPlus声明式API
  if (codeRowSet.includes('vue3ElementPlus_Options_API')) {
    zipFileList = _.concat(zipFileList, [
      {
        path: path.resolve(__dirname, '../cache/vue3ElementPlus/DrawerDemo/config/module.js'),
        name: `/${MODULE_VALUE}/vue3ElementPlus_Options_API/${MODULE_VALUE}/config/module.js`
      },
      {
        path: path.resolve(__dirname, '../cache/vue3ElementPlus/DrawerDemo/api/tableName.js'),
        name: `/${MODULE_VALUE}/vue3ElementPlus_Options_API/${MODULE_VALUE}/api/${MODULE_VALUE}.js`
      },
      {
        path: path.resolve(__dirname, '../cache/vue3ElementPlus/DrawerDemo/views/tableName/index.vue'),
        name: `/${MODULE_VALUE}/vue3ElementPlus_Options_API/${MODULE_VALUE}/views/${MODULE_VALUE}/index.vue`
      },
      {
        path: path.resolve(__dirname, '../cache/vue3ElementPlus/DrawerDemo/views/tableName/index.js'),
        name: `/${MODULE_VALUE}/vue3ElementPlus_Options_API/${MODULE_VALUE}/views/${MODULE_VALUE}/index.js`
      },
      {
        path: path.resolve(__dirname, '../cache/vue3ElementPlus/DrawerDemo/views/tableName/components/Drawer/index.vue'),
        name: `/${MODULE_VALUE}/vue3ElementPlus_Options_API/${MODULE_VALUE}/views/${MODULE_VALUE}/components/Drawer/index.vue`
      },
      {
        path: path.resolve(__dirname, '../cache/vue3ElementPlus/DrawerDemo/views/tableName/components/Drawer/footer.vue'),
        name: `/${MODULE_VALUE}/vue3ElementPlus_Options_API/${MODULE_VALUE}/views/${MODULE_VALUE}/components/Drawer/footer.vue`
      }
    ])
    switch (formCodeType) {
      // 使用StandardForm组件
      case 'StandardForm':
        // 有新增或修改操作时，才需要新增或修改组件
        if (hasCreate || hasUpdate) {
          zipFileList = _.concat(zipFileList, [
            {
              path: path.resolve(__dirname, '../cache/vue3ElementPlus/DrawerDemo/views/tableName/components/Drawer/createStandardForm.vue'),
              name: `/${MODULE_VALUE}/vue3ElementPlus_Options_API/${MODULE_VALUE}/views/${MODULE_VALUE}/components/Drawer/create.vue`
            }
          ])
        }
        // 有详情操作时，才需要详情组件
        if (hasDetail) {
          zipFileList = _.concat(zipFileList, [
            {
              path: path.resolve(__dirname, '../cache/vue3ElementPlus/DrawerDemo/views/tableName/components/Drawer/detailStandardForm.vue'),
              name: `/${MODULE_VALUE}/vue3ElementPlus_Options_API/${MODULE_VALUE}/views/${MODULE_VALUE}/components/Drawer/detail.vue`
            }
          ])
        }
        break
      // 按字段生成所有
      case 'all':
        // 有新增或修改操作时，才需要新增或修改组件
        if (hasCreate || hasUpdate) {
          zipFileList = _.concat(zipFileList, [
            {
              path: path.resolve(__dirname, '../cache/vue3ElementPlus/DrawerDemo/views/tableName/components/Drawer/create.vue'),
              name: `/${MODULE_VALUE}/vue3ElementPlus_Options_API/${MODULE_VALUE}/views/${MODULE_VALUE}/components/Drawer/create.vue`
            }
          ])
        }
        // 有详情操作时，才需要详情组件
        if (hasDetail) {
          zipFileList = _.concat(zipFileList, [
            {
              path: path.resolve(__dirname, '../cache/vue3ElementPlus/DrawerDemo/views/tableName/components/Drawer/detail.vue'),
              name: `/${MODULE_VALUE}/vue3ElementPlus_Options_API/${MODULE_VALUE}/views/${MODULE_VALUE}/components/Drawer/detail.vue`
            }
          ])
        }
        break
    }
  }

  res.zip(zipFileList, `${MODULE_VALUE}.zip`)

  /**
   * 以下代码为压缩为压缩包并存放磁盘
   */
  // // 创建可写流来写入数据
  // const zipFile = cachePath + `/${MODULE_VALUE}.zip`
  // const output = fs.createWriteStream(zipFile)
  // // 设置压缩级别
  // const archive = archiver('zip', {
  //   zlib: {
  //     level: 9
  //   }
  // })
  // // 建立管道连接
  // archive.pipe(output)
  // // 压缩目录到压缩包中 ： 将指定目录打包压缩到压缩包中，并且重命名为new-subdir，并且subdir目录下的所有文件仍然在new-subdir目录下，而不是在压缩包的根目录下
  // archive.directory(targetCodePath, `${MODULE_VALUE}`)
  // // 压缩目录到压缩包中 ： 将指定目录下的所有文件打包压缩到压缩包中，而这些文件在压缩包的根目录，而非子目录中
  // // archive.directory(targetCodePath, false)
  // // 完成压缩
  // archive.finalize()
}

// // 构造数据
// 生成代码
exports.genderCode = genderCode